home *** CD-ROM | disk | FTP | other *** search
/ Delphi Programmer's Power Pack / Delphi Volume 1.iso / e_to_l / edsspell / search.pas < prev    next >
Pascal/Delphi Source File  |  1996-09-15  |  5KB  |  157 lines

  1. unit Search;
  2.  
  3. interface
  4.  
  5. uses WinProcs, SysUtils, StdCtrls, Dialogs;
  6.  
  7. const
  8.   { Default word delimiters are any character except the core alphanumerics. }
  9.   WordDelimiters: set of Char = [#0..#255] - ['a'..'z','A'..'Z','1'..'9','0'];
  10.  
  11. { SearchMemo scans the text of a TEdit, TMemo, or other TCustomEdit-derived
  12.   component for a given search string.  The search starts at the current
  13.   caret position in the control.  The Options parameter determines whether the
  14.   search runs forward (frDown) or backward from the caret position, whether
  15.   or not the text comparison is case sensitive, and whether the matching
  16.   string must be a whole word.  If text is already selected in the control,
  17.   the search starts at the 'far end' of the selection (SelStart if searching
  18.   backwards, SelEnd if searching forwards).  If a match is found, the
  19.   control's text selection is changed to select the found text and the
  20.   function returns True.  If no match is found, the function returns False. }
  21. function SearchMemo(Memo: TCustomEdit;
  22.                     const SearchString: String;
  23.                     Options: TFindOptions): Boolean;
  24.  
  25. { SearchBuf is a lower-level search routine for arbitrary text buffers.  Same
  26.   rules as SearchMemo above.  If a match is found, the function returns a
  27.   pointer to the start of the matching string in the buffer.  If no match,
  28.   the function returns nil. }
  29. function SearchBuf(Buf: PChar; BufLen: Integer;
  30.                    SelStart, SelLength: Integer;
  31.                    SearchString: String;
  32.                    Options: TFindOptions): PChar;
  33.  
  34. implementation
  35.  
  36.  
  37. function SearchMemo(Memo: TCustomEdit;
  38.                     const SearchString: String;
  39.                     Options: TFindOptions): Boolean;
  40. var
  41.   Buffer, P: PChar;
  42.   Size: Word;
  43. begin
  44.   Result := False;
  45.   if (Length(SearchString) = 0) then Exit;
  46.   Size := Memo.GetTextLen;
  47.   if (Size = 0) then Exit;
  48.   Buffer := StrAlloc(Size + 1);
  49.   try
  50.     Memo.GetTextBuf(Buffer, Size + 1);
  51.     P := SearchBuf(Buffer, Size, Memo.SelStart, Memo.SelLength,
  52.                    SearchString, Options);
  53.     if P <> nil then
  54.     begin
  55.       Memo.SelStart := P - Buffer;
  56.       Memo.SelLength := Length(SearchString);
  57.       Result := True;
  58.     end;
  59.   finally
  60.     StrDispose(Buffer);
  61.   end;
  62. end;
  63.  
  64.  
  65. function SearchBuf(Buf: PChar; BufLen: Integer;
  66.                    SelStart, SelLength: Integer;
  67.                    SearchString: String;
  68.                    Options: TFindOptions): PChar;
  69. var
  70.   SearchCount, I: Integer;
  71.   C: Char;
  72.   Direction: Shortint;
  73.   CharMap: array [Char] of Char;
  74.  
  75.   function FindNextWordStart(var BufPtr: PChar): Boolean;
  76.   begin                   { (True XOR N) is equivalent to (not N) }
  77.     Result := False;      { (False XOR N) is equivalent to (N)    }
  78.      { When Direction is forward (1), skip non delimiters, then skip delimiters. }
  79.      { When Direction is backward (-1), skip delims, then skip non delims }
  80.     while (SearchCount > 0) and
  81.           ((Direction = 1) xor (BufPtr^ in WordDelimiters)) do
  82.     begin
  83.       Inc(BufPtr, Direction);
  84.       Dec(SearchCount);
  85.     end;
  86.     while (SearchCount > 0) and
  87.           ((Direction = -1) xor (BufPtr^ in WordDelimiters)) do
  88.     begin
  89.       Inc(BufPtr, Direction);
  90.       Dec(SearchCount);
  91.     end;
  92.     Result := SearchCount >= 0;
  93.     if (Direction = -1) and (BufPtr^ in WordDelimiters) then
  94.     begin   { back up one char, to leave ptr on first non delim }
  95.       Dec(BufPtr, Direction);
  96.       Inc(SearchCount);
  97.     end;
  98.   end;
  99.  
  100. begin
  101.   Result := nil;
  102.   if BufLen <= 0 then Exit;
  103.   if frDown in Options then
  104.   begin
  105.     Direction := 1;
  106.     Inc(SelStart, SelLength);  { start search past end of selection }
  107.     SearchCount := BufLen - SelStart - Length(SearchString);
  108. {    SearchCount := BufLen - SelStart - Length(SearchString) + 1;}
  109.     if SearchCount < 0 then Exit;
  110.     if Longint(SelStart) + SearchCount > BufLen then Exit;
  111.   end
  112.   else
  113.   begin
  114.     Direction := -1;
  115.     Dec(SelStart, Length(SearchString));
  116.     SearchCount := SelStart;
  117. {    SearchCount := SelStart + 1;}
  118.   end;
  119.   if (SelStart < 0) or (SelStart > BufLen) then Exit;
  120.   Result := @Buf[SelStart];
  121.  
  122.   { Using a Char map array is faster than calling AnsiUpper on every character }
  123.   for C := Low(CharMap) to High(CharMap) do
  124.     CharMap[C] := C;
  125.  
  126.   if not (frMatchCase in Options) then
  127.   begin
  128.     AnsiUpperBuff(PChar(@CharMap), sizeof(CharMap));
  129.     AnsiUpperBuff(@SearchString[1], Length(SearchString));
  130.   end;
  131.  
  132.   while SearchCount >= 0 do
  133.   begin
  134.     if frWholeWord in Options then
  135.       if not FindNextWordStart(Result) then Break;
  136.     I := 0;
  137.     while (CharMap[Result[I]] = SearchString[I+1]) do
  138.     begin
  139.       Inc(I);
  140.       if I >= Length(SearchString) then
  141.       begin
  142.         if (not (frWholeWord in Options)) or
  143.            (SearchCount = 0) or
  144.            (Result[I] in WordDelimiters) then
  145.           Exit;
  146.         Break;
  147.       end;
  148.     end;
  149.     Inc(Result, Direction);
  150.     Dec(SearchCount);
  151.   end;
  152.   Result := nil;
  153. end;
  154.  
  155. end.
  156.  
  157.